﻿using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using UIOC.CodeAnalysis.Extensions;
using UIOC.CodeAnalysis.Helpers;

namespace UIOC.CodeAnalysis.Generators;

internal static class BeanGenerator {

    public readonly struct GeneratedInfo(GeneratedSource source, INamedTypeSymbol objectSymbol) {

        public GeneratedSource GeneratedSource { get; } = source;

        public INamedTypeSymbol ObjectSymbol { get; } = objectSymbol;
    }

    public const string OBJECT_FIELD_NAME = "Object";
    public const string IS_DISPOSED_FIELD_NAME = "IsDisposed";

    public const string ASSERT_DISPOSE_METHOD_NAME = "AssertDispose";

    public const string BEAN_TYPE_SUFFIX = "__GeneratedBean";

    private static void LogInfo(string message) {
        SourceOutputHelper.LogInfo(DiagnosticSeverity.Info, $"[{nameof(BeanGenerator)}] {message}");
    }

    public static ImmutableDictionary<INamedTypeSymbol, GeneratedInfo> GenerateBean(
        Compilation compilation,
        ImmutableArray<ClassDeclarationSyntax> classDeclarationSyntaxes
    ) {
        var implementAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.Implement);
        if (implementAttributeSymbol == null) {
            return ImmutableDictionary<INamedTypeSymbol, GeneratedInfo>.Empty;
        }

        var instanceTypeAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.InstanceType);
        if (instanceTypeAttributeSymbol == null) {
            return ImmutableDictionary<INamedTypeSymbol, GeneratedInfo>.Empty;
        }

        var beanRegisterAttributeSymbol = compilation.GetTypeByMetadataName(AttributeFullNames.BeanRegister);
        if (beanRegisterAttributeSymbol == null) {
            return ImmutableDictionary<INamedTypeSymbol, GeneratedInfo>.Empty;
        }

        var defaultBeanClassSymbol = compilation.GetTypeByMetadataName(TypeFullNames.DefaultBean);
        if (defaultBeanClassSymbol == null) {
            return ImmutableDictionary<INamedTypeSymbol, GeneratedInfo>.Empty;
        }

        var singletonBeanClassSymbol = compilation.GetTypeByMetadataName(TypeFullNames.SingletonBean);
        if (singletonBeanClassSymbol == null) {
            return ImmutableDictionary<INamedTypeSymbol, GeneratedInfo>.Empty;
        }

        LogInfo($"[Assembly({compilation.Assembly.Name})] {nameof(GenerateBean)} ...");

        var result = ImmutableDictionary.CreateBuilder<INamedTypeSymbol, GeneratedInfo>(SymbolEqualityComparer.Default);

        var classSymbols = classDeclarationSyntaxes
            .Select(syntax => compilation.GetSemanticModel(syntax.SyntaxTree).GetDeclaredSymbol(syntax))
            .OfType<INamedTypeSymbol>()
            .Distinct(SymbolEqualityComparer.Default)
            .Cast<INamedTypeSymbol>()
            .ToList();

        foreach (var classSymbol in classSymbols) {
            var implementAttributeData = classSymbol.GetAttributes(implementAttributeSymbol).FirstOrDefault();
            if (implementAttributeData == null) {
                continue;
            }
            var instanceTypeAttributeData = classSymbol.AllInterfaces.SelectMany(@interface => @interface.GetAllAttributes(instanceTypeAttributeSymbol)).FirstOrDefault();
            var (compilationUnitSyntax, namespaceName) = GenerateBeanCompilationUnit(
                classSymbol,
                compilation,
                instanceTypeAttributeData,
                beanRegisterAttributeSymbol,
                defaultBeanClassSymbol,
                singletonBeanClassSymbol
            );
            if (compilationUnitSyntax == null) {
                continue;
            }
            var outputSource = SourceText.From(compilationUnitSyntax.NormalizeWhitespace().ToFullString(), Encoding.UTF8);
            var hintName = classSymbol.GetShortName().Replace("<", "(").Replace(">", ")");
            result.Add(classSymbol, new GeneratedInfo(
                new GeneratedSource(
                    hintName: hintName,
                    assemblyName: compilation.Assembly.Name,
                    namespaceName: namespaceName,
                    sourceText: outputSource,
                    compilationUnitSyntax: compilationUnitSyntax,
                    generatorName: nameof(BeanGenerator)
                ),
                classSymbol
            ));
        }
        return result.ToImmutable();
    }

    private static (CompilationUnitSyntax CompilationUnitSyntax, string NamespaceName) GenerateBeanCompilationUnit(
        INamedTypeSymbol classSymbol,
        Compilation compilation,
        AttributeData instanceTypeAttributeData,
        INamedTypeSymbol beanRegisterAttributeSymbol,
        INamedTypeSymbol defaultBeanClassSymbol,
        INamedTypeSymbol singletonBeanClassSymbol
    ) {
        var className = $"{classSymbol.Name}{BEAN_TYPE_SUFFIX}";

        var equalsMethodSyntax = GenerateEqualsMethod(classSymbol);
        var getHashCodeMethodSyntax = GenerateGetHashCodeMethod();

        var instanceTypeEnumValue = instanceTypeAttributeData?.ConstructorArguments.Length > 0
                   ? (int)instanceTypeAttributeData.ConstructorArguments[0].Value
                   : 0;
        var baseClassSymbol = instanceTypeEnumValue == 1
            ? singletonBeanClassSymbol
            : defaultBeanClassSymbol;

        var ifDirectiveSyntax = SyntaxFactory.IfDirectiveTrivia(
            SyntaxFactory.IdentifierName("!DISABLE_DEBUGGER_ATTRIBUTE"),
            isActive: true,
            branchTaken: true,
            conditionValue: true
        );
        var endIfDirectiveSyntax = SyntaxFactory.EndIfDirectiveTrivia(isActive: true);

        var classDeclarationSyntax = SyntaxFactory.ClassDeclaration(className)
            .AddModifiers(
                SyntaxFactory.Token(SyntaxKind.PrivateKeyword),
                SyntaxFactory.Token(SyntaxKind.SealedKeyword),
                SyntaxFactory.Token(SyntaxKind.PartialKeyword)
            )
            .AddAttributeLists(
                SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([
                    GenerateImplementAttribute(beanRegisterAttributeSymbol, classSymbol)
                ])),
                SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([
                    SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(
                        SyntaxFactory.IdentifierName($"global::{nameof(System)}.{nameof(System.Diagnostics)}"),
                        SyntaxFactory.IdentifierName(nameof(System.Diagnostics.DebuggerStepThroughAttribute))
                    )),
                ])).WithLeadingTrivia(
                    SyntaxFactory.Trivia(ifDirectiveSyntax),
                    SyntaxFactory.CarriageReturnLineFeed
                ),
                SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([
                    SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(
                        SyntaxFactory.IdentifierName($"global::{nameof(System)}.{nameof(System.Diagnostics)}"),
                        SyntaxFactory.IdentifierName(nameof(System.Diagnostics.DebuggerNonUserCodeAttribute))
                    )),
                ])).WithTrailingTrivia(
                    SyntaxFactory.CarriageReturnLineFeed,
                    SyntaxFactory.Trivia(endIfDirectiveSyntax)
                )
            )
            .AddBaseListTypes(
                baseClassSymbol.CreateSimpleBaseTypeSyntax()
            )
            .AddMembers(
                equalsMethodSyntax,
                getHashCodeMethodSyntax
            );

        if (classSymbol.TypeParameters.Length > 0) {
            classDeclarationSyntax = classDeclarationSyntax.AddTypeParameterListParameters([.. classSymbol.TypeParameters.Select(tp => SyntaxFactory.TypeParameter(tp.Name))]);
            classDeclarationSyntax = classDeclarationSyntax.AddConstraintClauses([.. classSymbol.GetConstraintClauses()]);
        }

        var registrationClassDeclarationSyntax = SyntaxFactory.ClassDeclaration(TypeRegistrationGenerator.CLASS_NAME_TYPE_REGISTRATIONS)
               .AddModifiers(
                   SyntaxFactory.Token(SyntaxKind.InternalKeyword),
                   SyntaxFactory.Token(SyntaxKind.StaticKeyword),
                   SyntaxFactory.Token(SyntaxKind.PartialKeyword)
               )
               .AddMembers(classDeclarationSyntax);

        var leadingTriviaSyntax = SyntaxFactory.Comment(UIOCGenerator.Leading);
        var namespaceName = compilation.Assembly.Name;
        var namespaceDeclarationSyntax = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.IdentifierName(namespaceName))
            .WithLeadingTrivia(leadingTriviaSyntax)
            .AddMembers(registrationClassDeclarationSyntax);

        var compilationUnit = SyntaxFactory.CompilationUnit()
            .AddMembers(namespaceDeclarationSyntax);

        return (compilationUnit, $"{namespaceName}__{TypeRegistrationGenerator.CLASS_NAME_TYPE_REGISTRATIONS}");
    }

    // 生成 [global::UIOC.Attributes.BeanRegister(typeof(global::{{NameSpace}}.{{ObjectClass}}))] 特性
    private static AttributeSyntax GenerateImplementAttribute(INamedTypeSymbol beanRegisterAttributeSymbol, INamedTypeSymbol classSymbol) {
        var typeOfExpressionSyntax = SyntaxFactory.TypeOfExpression(
            SyntaxFactory.ParseTypeName($"{classSymbol.Name}{ImplementationGenerator.IMPLEMENTATION_TYPE_SUFFIX}{(classSymbol.IsGenericType ? $"<{string.Join(", ", Enumerable.Range(1, classSymbol.Arity).Select(i => string.Empty))}>" : string.Empty)}")
        );
        return SyntaxFactory.Attribute(
            beanRegisterAttributeSymbol.CreateSimpleNameSyntax(),
            SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList([SyntaxFactory.AttributeArgument(typeOfExpressionSyntax)]))
        );
    }

    //private static FieldDeclarationSyntax GenerateObjectField(INamedTypeSymbol beanObjectInterfaceSymbol) {
    //    var variableTypeNameSyntax = beanObjectInterfaceSymbol.CreateSimpleNameSyntax();
    //    var variableDeclaratorSyntax = SyntaxFactory.VariableDeclarator(OBJECT_FIELD_NAME);
    //    var variableDeclarationSyntax = SyntaxFactory.VariableDeclaration(variableTypeNameSyntax)
    //        .AddVariables(variableDeclaratorSyntax);
    //    var fieldDeclarationSyntax = SyntaxFactory.FieldDeclaration(variableDeclarationSyntax)
    //        .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword));
    //    return fieldDeclarationSyntax;
    //}

    //private static PropertyDeclarationSyntax GenerateObjectProperty(
    //    INamedTypeSymbol beanObjectInterfaceSymbol,
    //    INamedTypeSymbol beanInterfaceSymbol
    //) {
    //    var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
    //        .WithBody(SyntaxFactory.Block(
    //            GenerateAssertDisposeExpressionStatement(),
    //            SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME))
    //        ))
    //        .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None));

    //    var explicitInterfaceSpecification = SyntaxFactory.ExplicitInterfaceSpecifier(beanInterfaceSymbol.CreateSimpleNameSyntax());
    //    var propertyTypeNameSyntax = beanObjectInterfaceSymbol.CreateSimpleNameSyntax();
    //    var propertyDeclarationSyntax = SyntaxFactory.PropertyDeclaration(
    //        propertyTypeNameSyntax,
    //        MemberNames.IBeanInternal.Object
    //    )
    //        .WithExplicitInterfaceSpecifier(explicitInterfaceSpecification)
    //        .AddAccessorListAccessors(getAccessor);
    //    return propertyDeclarationSyntax;
    //}

    //private static FieldDeclarationSyntax GenerateIsDisposedField() {
    //    var variableTypeNameSyntax = SyntaxFactory.IdentifierName("bool");
    //    var variableDeclaratorSyntax = SyntaxFactory.VariableDeclarator(IS_DISPOSED_FIELD_NAME);
    //    var variableDeclarationSyntax = SyntaxFactory.VariableDeclaration(variableTypeNameSyntax)
    //        .AddVariables(variableDeclaratorSyntax);
    //    var fieldDeclarationSyntax = SyntaxFactory.FieldDeclaration(variableDeclarationSyntax)
    //        .AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword));
    //    return fieldDeclarationSyntax;
    //}

    //private static PropertyDeclarationSyntax GenerateIsDisposedProperty(INamedTypeSymbol objectInterfaceSymbol) {
    //    var getAccessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration)
    //        .WithBody(SyntaxFactory.Block(
    //            GenerateAssertDisposeExpressionStatement(),
    //            SyntaxFactory.ReturnStatement(SyntaxFactory.IdentifierName(IS_DISPOSED_FIELD_NAME))
    //        ))
    //        .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None));
    //    var explicitInterfaceSpecification = SyntaxFactory.ExplicitInterfaceSpecifier(objectInterfaceSymbol.CreateSimpleNameSyntax());
    //    var propertyTypeNameSyntax = SyntaxFactory.IdentifierName("bool");
    //    var propertyDeclarationSyntax = SyntaxFactory.PropertyDeclaration(
    //        propertyTypeNameSyntax,
    //        MemberNames.IObject.IsDisposed
    //    )
    //        .WithExplicitInterfaceSpecifier(explicitInterfaceSpecification)
    //        .AddAccessorListAccessors(getAccessor);
    //    return propertyDeclarationSyntax;
    //}

    //private static MethodDeclarationSyntax GenerateDisposeMethod(INamedTypeSymbol objectInternalInterfaceSymbol) {
    //    var nullExpression = SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression);
    //    var objectAssignmentStatement = SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
    //        SyntaxKind.SimpleAssignmentExpression,
    //        SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME),
    //        nullExpression
    //    )).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));

    //    var trueExpression = SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression);
    //    var isDisposedAssignmentStatement = SyntaxFactory.ExpressionStatement(SyntaxFactory.AssignmentExpression(
    //        SyntaxKind.SimpleAssignmentExpression,
    //        SyntaxFactory.IdentifierName(IS_DISPOSED_FIELD_NAME),
    //        trueExpression
    //    )).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));

    //    var explicitInterfaceSpecification = SyntaxFactory.ExplicitInterfaceSpecifier(objectInternalInterfaceSymbol.CreateSimpleNameSyntax());
    //    var methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(
    //        SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)),
    //        MemberNames.IBeanInternal.Dispose
    //    ).WithExplicitInterfaceSpecifier(
    //        explicitInterfaceSpecification
    //    ).WithBody(SyntaxFactory.Block(
    //        GenerateAssertDisposeExpressionStatement(),
    //        objectAssignmentStatement,
    //        isDisposedAssignmentStatement
    //    ));
    //    return methodDeclarationSyntax;
    //}

    //private static MethodDeclarationSyntax GenerateAssertDisposeMethod() {
    //    var conditional = SyntaxFactory.QualifiedName(
    //        left: SyntaxFactory.IdentifierName($"global::{nameof(System)}.{nameof(System.Diagnostics)}"),
    //        right: SyntaxFactory.IdentifierName(nameof(System.Diagnostics.ConditionalAttribute))
    //    );
    //    var attributes = SyntaxFactory.List([
    //        SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([SyntaxFactory.Attribute(
    //            conditional,
    //            SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList([
    //                SyntaxFactory.AttributeArgument(
    //                    SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,  SyntaxFactory.Literal("UNITY_EDITOR"))
    //                )
    //            ]))
    //        )])),
    //        SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([SyntaxFactory.Attribute(
    //            conditional,
    //            SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList([
    //                SyntaxFactory.AttributeArgument(
    //                    SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression,  SyntaxFactory.Literal("DEBUG"))
    //                )
    //            ]))
    //        )])),
    //        SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList([SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(
    //            SyntaxFactory.IdentifierName($"global::{nameof(System)}.{nameof(System.Diagnostics)}"),
    //            SyntaxFactory.IdentifierName(nameof(System.Diagnostics.DebuggerHiddenAttribute))
    //        ))])),
    //    ]);

    //    var exceptionMessage = SyntaxFactory.LiteralExpression(
    //        kind: SyntaxKind.StringLiteralExpression,
    //        token: SyntaxFactory.Literal("正在使用已经销毁的对象")
    //    );

    //    var exceptionCreation = SyntaxFactory.ObjectCreationExpression(
    //        type: SyntaxFactory.QualifiedName(
    //            left: SyntaxFactory.IdentifierName($"global::{nameof(System)}"),
    //            right: SyntaxFactory.IdentifierName(nameof(System.ObjectDisposedException))
    //        ),
    //        argumentList: SyntaxFactory.ArgumentList(
    //            arguments: SyntaxFactory.SeparatedList([
    //                SyntaxFactory.Argument(exceptionMessage)
    //            ])
    //        ),
    //        initializer: null
    //    );

    //    var throwStatement = SyntaxFactory.ThrowStatement(exceptionCreation);

    //    var ifStatement = SyntaxFactory.IfStatement(
    //        condition: SyntaxFactory.IdentifierName(IS_DISPOSED_FIELD_NAME),
    //        statement: SyntaxFactory.Block(throwStatement)
    //    );

    //    var methodBody = SyntaxFactory.Block(ifStatement);

    //    return SyntaxFactory.MethodDeclaration(
    //        returnType: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)),
    //        identifier: ASSERT_DISPOSE_METHOD_NAME
    //    )
    //    .WithAttributeLists(attributes)
    //    .WithModifiers(SyntaxFactory.TokenList(
    //        SyntaxFactory.Token(SyntaxKind.PrivateKeyword)
    //    ))
    //    .WithParameterList(SyntaxFactory.ParameterList())
    //    .WithBody(methodBody);
    //}

    //private static MethodDeclarationSyntax GenerateInitializeMethod(
    //    INamedTypeSymbol beanInterfaceSymbol,
    //    INamedTypeSymbol beanObjectInterfaceSymbol
    //) {
    //    var objectParameterName = "@object";
    //    var objectParameter = SyntaxFactory.Parameter(
    //        attributeLists: default,
    //        modifiers: default,
    //        type: beanObjectInterfaceSymbol.CreateSimpleNameSyntax(),
    //        identifier: SyntaxFactory.Identifier(objectParameterName),
    //        @default: null
    //    );

    //    var objectAssignmentStatement = SyntaxFactory.ExpressionStatement(
    //        SyntaxFactory.AssignmentExpression(
    //            SyntaxKind.SimpleAssignmentExpression,
    //            SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME),
    //            SyntaxFactory.IdentifierName(objectParameterName)
    //        )
    //    ).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));

    //    var explicitInterfaceSpecification = SyntaxFactory.ExplicitInterfaceSpecifier(beanInterfaceSymbol.CreateSimpleNameSyntax());
    //    var methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(
    //        SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)),
    //        MemberNames.IBeanInternal.Initialize
    //   ).WithExplicitInterfaceSpecifier(
    //        explicitInterfaceSpecification
    //   ).AddParameterListParameters(
    //        objectParameter
    //   ).WithBody(SyntaxFactory.Block(
    //       GenerateAssertDisposeExpressionStatement(),
    //       objectAssignmentStatement
    //    ));
    //    return methodDeclarationSyntax;
    //}

    //private static MethodDeclarationSyntax GenerateToStringMethod() {
    //    var returnStatement = SyntaxFactory.ReturnStatement(
    //        expression: SyntaxFactory.InvocationExpression(
    //            expression: SyntaxFactory.MemberAccessExpression(
    //                kind: SyntaxKind.SimpleMemberAccessExpression,
    //                expression: SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME),
    //                operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
    //                name: SyntaxFactory.IdentifierName("ToString")
    //            ),
    //            argumentList: SyntaxFactory.ArgumentList()
    //        )
    //    );

    //    var methodBody = SyntaxFactory.Block(
    //        GenerateAssertDisposeExpressionStatement(),
    //        returnStatement
    //    );

    //    return SyntaxFactory.MethodDeclaration(
    //        returnType: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.StringKeyword)),
    //        identifier: "ToString"
    //    )
    //    .WithModifiers(SyntaxFactory.TokenList(
    //        SyntaxFactory.Token(SyntaxKind.PublicKeyword),
    //        SyntaxFactory.Token(SyntaxKind.OverrideKeyword)
    //    ))
    //    .WithParameterList(SyntaxFactory.ParameterList())
    //    .WithBody(methodBody);
    //}

    //private static MethodDeclarationSyntax GenerateGetBeanHashCodeMethod(INamedTypeSymbol beanInterfaceSymbol) {
    //    var explicitInterfaceSpecification = SyntaxFactory.ExplicitInterfaceSpecifier(beanInterfaceSymbol.CreateSimpleNameSyntax());
    //    var returnStatement = SyntaxFactory.ReturnStatement(
    //        expression: SyntaxFactory.InvocationExpression(
    //            expression: SyntaxFactory.MemberAccessExpression(
    //                kind: SyntaxKind.SimpleMemberAccessExpression,
    //                expression: SyntaxFactory.IdentifierName("base"),
    //                operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
    //                name: SyntaxFactory.IdentifierName("GetHashCode")
    //            ),
    //            argumentList: SyntaxFactory.ArgumentList()
    //        )
    //    );
    //    var methodBody = SyntaxFactory.Block(
    //        returnStatement
    //    );
    //    return SyntaxFactory.MethodDeclaration(
    //        returnType: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)),
    //        identifier: MemberNames.IBeanInternal.GetBeanHashCode
    //    )
    //    .WithExplicitInterfaceSpecifier(explicitInterfaceSpecification)
    //    .WithParameterList(SyntaxFactory.ParameterList())
    //    .WithBody(methodBody);
    //}

    private static MethodDeclarationSyntax GenerateGetHashCodeMethod() {
        var returnStatement = SyntaxFactory.ReturnStatement(
            expression: SyntaxFactory.InvocationExpression(
                expression: SyntaxFactory.MemberAccessExpression(
                    kind: SyntaxKind.SimpleMemberAccessExpression,
                    expression: SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME),
                    operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
                    name: SyntaxFactory.IdentifierName("GetHashCode")
                ),
                argumentList: SyntaxFactory.ArgumentList()
            )
        );

        var methodBody = SyntaxFactory.Block(
            GenerateAssertDisposeExpressionStatement(),
            returnStatement
        );

        return SyntaxFactory.MethodDeclaration(
            returnType: SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword)),
            identifier: "GetHashCode"
        )
        .WithModifiers(SyntaxFactory.TokenList(
            SyntaxFactory.Token(SyntaxKind.PublicKeyword),
            SyntaxFactory.Token(SyntaxKind.OverrideKeyword)
        ))
        .WithParameterList(SyntaxFactory.ParameterList())
        .WithBody(methodBody);
    }

    //private static MethodDeclarationSyntax GenerateBeanEqualsMethod(INamedTypeSymbol beanInterfaceSymbol) {
    //    var explicitInterfaceSpecification = SyntaxFactory.ExplicitInterfaceSpecifier(beanInterfaceSymbol.CreateSimpleNameSyntax());
    //    var objectParameterName = "obj";
    //    var objectParameter = SyntaxFactory.Parameter(
    //        attributeLists: default,
    //        modifiers: default,
    //        type: beanInterfaceSymbol.CreateSimpleNameSyntax(),
    //        identifier: SyntaxFactory.Identifier(objectParameterName),
    //        @default: null
    //    );
    //    var returnStatement = SyntaxFactory.ReturnStatement(
    //        expression: SyntaxFactory.InvocationExpression(
    //            expression: SyntaxFactory.MemberAccessExpression(
    //                kind: SyntaxKind.SimpleMemberAccessExpression,
    //                expression: SyntaxFactory.IdentifierName("base"),
    //                operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
    //                name: SyntaxFactory.IdentifierName("Equals")
    //            ),
    //            argumentList: SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList([
    //                SyntaxFactory.Argument(SyntaxFactory.IdentifierName(objectParameterName))
    //            ]))
    //        )
    //    );
    //    var methodBody = SyntaxFactory.Block(
    //        returnStatement
    //    );
    //    var methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(
    //        SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)),
    //        MemberNames.IBeanInternal.BeanEquals
    //    ).AddParameterListParameters(
    //        objectParameter
    //    ).WithExplicitInterfaceSpecifier(
    //        explicitInterfaceSpecification
    //    ).WithBody(methodBody);
    //    return methodDeclarationSyntax;
    //}

    private static MethodDeclarationSyntax GenerateEqualsMethod(
        INamedTypeSymbol classSymbol
    ) {
        var objectParameterName = "obj";
        var objectParameter = SyntaxFactory.Parameter(
            attributeLists: default,
            modifiers: default,
            type: SyntaxFactory.IdentifierName("object"),
            identifier: SyntaxFactory.Identifier(objectParameterName),
            @default: null
        );

        var pattern = SyntaxFactory.DeclarationPattern(
            SyntaxFactory.IdentifierName(classSymbol.GetShortName(true).Replace(classSymbol.Name, $"{classSymbol.Name}{BEAN_TYPE_SUFFIX}")),
            SyntaxFactory.SingleVariableDesignation(SyntaxFactory.Identifier("other"))
        );
        var isExpression = SyntaxFactory.IsPatternExpression(
            SyntaxFactory.IdentifierName("obj"),
            pattern
        );

        var notDisposedCheck = SyntaxFactory.PrefixUnaryExpression(
            kind: SyntaxKind.LogicalNotExpression,
            operand: SyntaxFactory.MemberAccessExpression(
                kind: SyntaxKind.SimpleMemberAccessExpression,
                expression: SyntaxFactory.IdentifierName("other"),
                operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
                name: SyntaxFactory.IdentifierName(IS_DISPOSED_FIELD_NAME)
            )
        );

        var objectEqualsCheck = SyntaxFactory.InvocationExpression(
            expression: SyntaxFactory.MemberAccessExpression(
                kind: SyntaxKind.SimpleMemberAccessExpression,
                expression: SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME),
                operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
                name: SyntaxFactory.IdentifierName("Equals")
            ),
            argumentList: SyntaxFactory.ArgumentList(
                arguments: SyntaxFactory.SeparatedList([
                    SyntaxFactory.Argument(
                        expression: SyntaxFactory.MemberAccessExpression(
                            kind: SyntaxKind.SimpleMemberAccessExpression,
                            expression: SyntaxFactory.IdentifierName("other"),
                            operatorToken: SyntaxFactory.Token(SyntaxKind.DotToken),
                            name: SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME)
                        )
                    )
                ])
            )
        );

        var condition = SyntaxFactory.BinaryExpression(
            kind: SyntaxKind.LogicalAndExpression,
            left: SyntaxFactory.BinaryExpression(
                kind: SyntaxKind.LogicalAndExpression,
                left: isExpression,
                right: notDisposedCheck
            ),
            right: objectEqualsCheck
        );

        var ifStatement = SyntaxFactory.IfStatement(
            condition: condition,
            statement: SyntaxFactory.Block(
                SyntaxFactory.ReturnStatement(
                    SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression)
                )
            )
        );

        var defaultReturn = SyntaxFactory.ReturnStatement(
            expression: SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression)
        );

        var methodBody = SyntaxFactory.Block(
            GenerateAssertDisposeExpressionStatement(),
            ifStatement,
            defaultReturn
        );

        var methodDeclarationSyntax = SyntaxFactory.MethodDeclaration(
            SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)),
            "Equals"
        ).AddParameterListParameters(
            objectParameter
        ).AddModifiers(
            SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.OverrideKeyword)
        ).WithBody(methodBody);
        return methodDeclarationSyntax;
    }

    public static ExpressionStatementSyntax GenerateAssertDisposeExpressionStatement() {
        return SyntaxFactory.ExpressionStatement(
            SyntaxFactory.InvocationExpression(
                SyntaxFactory.IdentifierName(ASSERT_DISPOSE_METHOD_NAME),
                SyntaxFactory.ArgumentList()
            )
        );
    }

    //public static MethodDeclarationSyntax ReplaceAllocateMethodBody(
    //    MethodDeclarationSyntax allocateMethodSyntax,
    //    INamedTypeSymbol pooledObjectInternalInterfaceSymbol
    //) {
    //    return allocateMethodSyntax.WithBody(allocateMethodSyntax.Body.WithStatements(
    //        allocateMethodSyntax.Body.Statements.Replace(allocateMethodSyntax.Body.Statements[1], SyntaxFactory.IfStatement(
    //            SyntaxFactory.IsPatternExpression(
    //                SyntaxFactory.IdentifierName(OBJECT_FIELD_NAME),
    //                SyntaxFactory.DeclarationPattern(
    //                    pooledObjectInternalInterfaceSymbol.CreateSimpleNameSyntax(),
    //                    SyntaxFactory.SingleVariableDesignation(SyntaxFactory.Identifier("pooledObject"))
    //                )
    //            ),
    //            SyntaxFactory.Block(SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression(
    //                SyntaxFactory.MemberAccessExpression(
    //                    SyntaxKind.SimpleMemberAccessExpression,
    //                    SyntaxFactory.IdentifierName("pooledObject"),
    //                    SyntaxFactory.IdentifierName(MemberNames.IPoolObjectInternal.Allocate)
    //                )
    //            )))
    //        )
    //    )));
    //}
}
